"""DBus helper utilities for BLE GATT Server."""

import logging
import dbus
import dbus.service
import dbus.mainloop.glib
from typing import Optional, Dict, Any, List
from gi.repository import GLib

from src.ble.constants import (
    DBUS_SERVICE_NAME,
    DBUS_BLUEZ_OBJECT_PATH,
    DBUS_ADAPTER_INTERFACE,
    DBUS_ADAPTER_PATH_PREFIX,
    DBUS_GATT_MANAGER_INTERFACE,
    DBUS_LE_ADVERTISING_MANAGER_INTERFACE
)

logger = logging.getLogger(__name__)


class DBusHelper:
    """Helper class for DBus operations."""
    
    def __init__(self):
        """Initialize DBus helper."""
        dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
        self.bus = dbus.SystemBus()
        self._adapter = None
        self._adapter_props = None
        
    def get_bus(self) -> dbus.SystemBus:
        """Get the system DBus instance."""
        return self.bus
        
    def get_adapter(self, adapter_name: str = "hci0") -> Optional[dbus.Interface]:
        """
        Get the Bluetooth adapter interface.
        
        Args:
            adapter_name: Name of the adapter (default: hci0)
            
        Returns:
            DBus interface for the adapter or None if not found
        """
        if self._adapter:
            return self._adapter
            
        try:
            adapter_path = f"{DBUS_ADAPTER_PATH_PREFIX}0"
            adapter_obj = self.bus.get_object(DBUS_SERVICE_NAME, adapter_path)
            self._adapter = dbus.Interface(adapter_obj, DBUS_ADAPTER_INTERFACE)
            self._adapter_props = dbus.Interface(adapter_obj, dbus.PROPERTIES_IFACE)
            
            # Power on the adapter
            self._adapter_props.Set(DBUS_ADAPTER_INTERFACE, "Powered", dbus.Boolean(True))
            
            # Set adapter as discoverable and pairable
            self._adapter_props.Set(DBUS_ADAPTER_INTERFACE, "Discoverable", dbus.Boolean(True))
            self._adapter_props.Set(DBUS_ADAPTER_INTERFACE, "Pairable", dbus.Boolean(True))
            
            logger.info(f"Adapter {adapter_name} initialized and powered on")
            return self._adapter
            
        except dbus.DBusException as e:
            logger.error(f"Failed to get adapter {adapter_name}: {e}")
            return None
            
    def get_adapter_properties(self) -> Optional[Dict[str, Any]]:
        """Get adapter properties."""
        if not self._adapter_props:
            self.get_adapter()
            
        if self._adapter_props:
            try:
                return self._adapter_props.GetAll(DBUS_ADAPTER_INTERFACE)
            except dbus.DBusException as e:
                logger.error(f"Failed to get adapter properties: {e}")
                
        return None
        
    def get_gatt_manager(self) -> Optional[dbus.Interface]:
        """Get the GATT Manager interface."""
        adapter = self.get_adapter()
        if not adapter:
            return None
            
        try:
            adapter_path = adapter.object_path
            manager_obj = self.bus.get_object(DBUS_SERVICE_NAME, adapter_path)
            return dbus.Interface(manager_obj, DBUS_GATT_MANAGER_INTERFACE)
        except dbus.DBusException as e:
            logger.error(f"Failed to get GATT Manager: {e}")
            return None
            
    def get_advertising_manager(self) -> Optional[dbus.Interface]:
        """Get the LE Advertising Manager interface."""
        adapter = self.get_adapter()
        if not adapter:
            return None
            
        try:
            adapter_path = adapter.object_path
            manager_obj = self.bus.get_object(DBUS_SERVICE_NAME, adapter_path)
            return dbus.Interface(manager_obj, DBUS_LE_ADVERTISING_MANAGER_INTERFACE)
        except dbus.DBusException as e:
            logger.error(f"Failed to get Advertising Manager: {e}")
            return None
            
    def get_managed_objects(self) -> Dict[str, Any]:
        """Get all managed objects from BlueZ."""
        try:
            manager = dbus.Interface(
                self.bus.get_object(DBUS_SERVICE_NAME, "/"),
                "org.freedesktop.DBus.ObjectManager"
            )
            return manager.GetManagedObjects()
        except dbus.DBusException as e:
            logger.error(f"Failed to get managed objects: {e}")
            return {}
            
    def find_adapter(self) -> Optional[str]:
        """Find the first available Bluetooth adapter."""
        objects = self.get_managed_objects()
        
        for path, interfaces in objects.items():
            if DBUS_ADAPTER_INTERFACE in interfaces:
                logger.info(f"Found adapter at {path}")
                return path
                
        logger.error("No Bluetooth adapter found")
        return None
        
    def get_device_address(self) -> Optional[str]:
        """Get the Bluetooth adapter MAC address."""
        props = self.get_adapter_properties()
        if props and "Address" in props:
            return str(props["Address"])
        return None
        
    def set_alias(self, alias: str) -> bool:
        """
        Set the adapter alias (device name).
        
        Args:
            alias: The alias to set
            
        Returns:
            True if successful, False otherwise
        """
        if not self._adapter_props:
            self.get_adapter()
            
        if self._adapter_props:
            try:
                self._adapter_props.Set(DBUS_ADAPTER_INTERFACE, "Alias", alias)
                logger.info(f"Adapter alias set to: {alias}")
                return True
            except dbus.DBusException as e:
                logger.error(f"Failed to set adapter alias: {e}")
                
        return False


# Module-level convenience functions
_helper_instance = None


def get_dbus_helper() -> DBusHelper:
    """Get or create a singleton DBusHelper instance."""
    global _helper_instance
    if _helper_instance is None:
        _helper_instance = DBusHelper()
    return _helper_instance


def get_adapter(adapter_name: str = "hci0") -> Optional[dbus.Interface]:
    """Convenience function to get adapter."""
    return get_dbus_helper().get_adapter(adapter_name)


def get_managed_objects() -> Dict[str, Any]:
    """Convenience function to get managed objects."""
    return get_dbus_helper().get_managed_objects()


def get_device_mac() -> Optional[str]:
    """Get the device MAC address."""
    return get_dbus_helper().get_device_address()